<!-- Metrics Dashboard Tab -->
<div id="tabMetricsDashboard" class="tab-content">
    <div class="endpoint-section">
        <h2>System Metrics Dashboard</h2>
        <p>Real-time monitoring of system performance and health metrics.</p>

        <div class="metrics-controls">
            <button class="btn btn-primary" id="metrics-refresh">
                Refresh Metrics
            </button>
            <button class="btn btn-secondary" id="metrics-auto-start">
                Auto-Refresh (5s)
            </button>
            <button class="btn btn-secondary" id="metrics-auto-stop">
                Stop Auto-Refresh
            </button>
        </div>

        <div class="metrics-grid mt-3">
            <div class="metric-card">
                <h3>API Health</h3>
                <div class="metric-value" id="metric-api-health">--</div>
                <div class="metric-label">Status</div>
            </div>

            <div class="metric-card">
                <h3>Request Rate</h3>
                <div class="metric-value" id="metric-request-rate">--</div>
                <div class="metric-label">req/min</div>
            </div>

            <div class="metric-card">
                <h3>Response Time</h3>
                <div class="metric-value" id="metric-response-time">--</div>
                <div class="metric-label">ms (p50)</div>
            </div>

            <div class="metric-card">
                <h3>Active Connections</h3>
                <div class="metric-value" id="metric-active-connections">--</div>
                <div class="metric-label">connections</div>
            </div>

            <div class="metric-card">
                <h3>Error Rate</h3>
                <div class="metric-value" id="metric-error-rate">--</div>
                <div class="metric-label">errors/min</div>
            </div>

            <div class="metric-card">
                <h3>CPU Usage</h3>
                <div class="metric-value" id="metric-cpu-usage">--</div>
                <div class="metric-label">percent</div>
            </div>

            <div class="metric-card">
                <h3>Memory Usage</h3>
                <div class="metric-value" id="metric-memory-usage">--</div>
                <div class="metric-label">MB</div>
            </div>

            <div class="metric-card">
                <h3>Database Size</h3>
                <div class="metric-value" id="metric-database-size">--</div>
                <div class="metric-label">MB</div>
            </div>

            <div class="metric-card" style="grid-column: span 2; text-align: left;">
                <h3>Jobs Overview</h3>
                <div class="metric-label">Top 3 busiest queues</div>
                <ul id="metric-jobs-overview" style="margin-top: 8px; padding-left: 18px;">
                    <li class="text-muted">No data</li>
                </ul>
            </div>
        </div>

        <div class="endpoint-section" style="margin-top:16px;">
            <h3>
                Embeddings Orchestrator
                <span id="orchestrator_sse_status" class="badge badge-secondary" title="SSE connection status" style="margin-right:6px;">disconnected</span>
                <span id="orchestrator_fallback_badge" class="badge badge-warn" style="display:none;" title="Polling summary returned zeroed snapshot (Redis/Orchestrator unavailable)">fallback</span>
                <span id="orchestrator_fallback_hint" class="text-muted" style="display:none; font-size: 12px;">(zeroed payload; ages/flags unavailable)
                    <a href="#" data-action="open-monitoring-docs" title="Open Monitoring README for guidance" style="margin-left:6px; text-decoration: underline;">learn more</a>
                </span>
            </h3>
            <div class="metrics-controls">
                <button class="btn btn-primary" id="orchestrator-refresh">Refresh</button>
                <button class="btn btn-secondary" id="orchestrator-auto-start">Auto-Refresh (10s)</button>
                <button class="btn btn-secondary" id="orchestrator-auto-stop">Stop</button>
                <label style="margin-left:8px;">
                    <input type="checkbox" id="orchestrator_live_sse"> Live (SSE)
                </label>
            </div>
            <div class="table-responsive mt-2">
                <table class="data-table">
                    <thead>
                        <tr>
                            <th>Stage</th>
                            <th>Queue Depth</th>
                            <th>DLQ Depth</th>
                            <th>DLQ Sparkline</th>
                            <th>DLQ Rate (est/s)</th>
                            <th>Age Sparkline</th>
                            <th>Flags</th>
                            <th>Processed (sum)</th>
                            <th>Processed/s</th>
                            <th>Failed (sum)</th>
                            <th>Failed/s</th>
                        </tr>
                    </thead>
                    <tbody id="orchestrator_tableBody">
                        <tr><td colspan="8" class="text-muted">No data</td></tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

<!-- Metrics Endpoints Tab -->
<div id="tabMetricsEndpoints" class="tab-content">
    <div class="endpoint-section" id="metricsPrometheus">
        <h2>
            <span class="endpoint-method get">GET</span>
            <span class="endpoint-path">/metrics - Prometheus Metrics</span>
        </h2>
        <p>Get metrics in Prometheus format for scraping by monitoring systems.</p>

        <button class="api-button" data-action="exec-endpoint" data-id="metricsPrometheus" data-method="GET" data-path="/metrics" data-body="none">
            Get Prometheus Metrics
        </button>

        <h3>cURL Command:</h3>
        <pre id="metricsPrometheus_curl">---</pre>

        <h3>Response (Prometheus Format):</h3>
        <pre id="metricsPrometheus_response">---</pre>
    </div>

    <div class="endpoint-section" id="metricsJSON">
        <h2>
            <span class="endpoint-method get">GET</span>
            <span class="endpoint-path">/api/v1/metrics - JSON Metrics</span>
        </h2>
        <p>Get current metrics in JSON format for custom monitoring solutions.</p>

        <div class="form-group">
            <label for="metricsJSON_category">Metric Category:</label>
            <select id="metricsJSON_category">
                <option value="">All Metrics</option>
                <option value="system">System Metrics</option>
                <option value="api">API Metrics</option>
                <option value="database">Database Metrics</option>
                <option value="cache">Cache Metrics</option>
                <option value="llm">LLM Metrics</option>
            </select>
        </div>

        <button class="api-button" data-action="exec-endpoint" data-id="metricsJSON" data-method="GET" data-path="/api/v1/metrics" data-body="query">
            Get JSON Metrics
        </button>

        <h3>cURL Command:</h3>
        <pre id="metricsJSON_curl">---</pre>

        <h3>Response:</h3>
        <pre id="metricsJSON_response">---</pre>
    </div>

    <div class="endpoint-section" id="metricsHealth">
        <h2>
            <span class="endpoint-method get">GET</span>
            <span class="endpoint-path">/api/v1/metrics/health - Metrics Service Health</span>
        </h2>
        <p>Check the health status of the metrics collection service.</p>

        <button class="api-button" data-action="exec-endpoint" data-id="metricsHealth" data-method="GET" data-path="/api/v1/metrics/health" data-body="none">
            Check Metrics Health
        </button>

        <h3>cURL Command:</h3>
        <pre id="metricsHealth_curl">---</pre>

        <h3>Response:</h3>
        <pre id="metricsHealth_response">---</pre>
    </div>

    <div class="endpoint-section" id="metricsReset">
        <h2>
            <span class="endpoint-method post">POST</span>
            <span class="endpoint-path">/api/v1/metrics/reset - Reset Metrics</span>
        </h2>
        <p>Reset metrics counters. Use with caution in production.</p>

        <div class="form-group">
            <label for="metricsReset_payload">Reset Configuration (JSON):</label>
            <textarea id="metricsReset_payload" class="code-input" rows="10">{
  "categories": ["api", "cache"],
  "preserve_history": true,
  "reset_timestamp": true,
  "reason": "Manual reset for testing",
  "admin_token": "your_admin_token"
}</textarea>
        </div>

        <button class="api-button btn-warning" data-action="exec-endpoint" data-id="metricsReset" data-method="POST" data-path="/api/v1/metrics/reset" data-body="json" data-confirm="Are you sure you want to reset metrics?">
            Reset Metrics
        </button>

        <h3>cURL Command:</h3>
        <pre id="metricsReset_curl">---</pre>

        <h3>Response:</h3>
        <pre id="metricsReset_response">---</pre>
    </div>

    <div class="endpoint-section" id="jobsQueueStatsPanel">
        <h2>
            <span class="endpoint-method get">GET</span>
            <span class="endpoint-path">/api/v1/jobs/stats - Jobs Queue Stats</span>
        </h2>
        <p>View queue depth and processing counts per domain/queue/job_type. Auto-refresh supported.</p>

        <div class="columns">
            <div class="column">
                <div class="form-group">
                    <label for="jobsStats_domain">Domain:</label>
                    <input type="text" id="jobsStats_domain" placeholder="e.g., chatbooks">
                </div>
            </div>
            <div class="column">
                <div class="form-group">
                    <label for="jobsStats_queue">Queue:</label>
                    <select id="jobsStats_queue">
                        <option value="">(any)</option>
                        <option value="default">default</option>
                        <option value="high">high</option>
                        <option value="low">low</option>
                    </select>
                </div>
            </div>
            <div class="column">
                <div class="form-group">
                    <label for="jobsStats_jobType">Job Type:</label>
                    <input type="text" id="jobsStats_jobType" placeholder="e.g., export">
                </div>
            </div>
        </div>

        <div class="metrics-controls">
            <button class="btn btn-primary" id="jobsStats_refresh">Refresh</button>
            <button class="btn btn-secondary" id="jobsStats_auto_start">Auto-Refresh (10s)</button>
            <button class="btn btn-secondary" id="jobsStats_auto_stop">Stop</button>
        </div>

        <div class="table-responsive mt-2">
            <table class="data-table">
                <thead>
                    <tr>
                        <th>Domain</th>
                        <th>Queue</th>
                        <th>Job Type</th>
                        <th>Queued</th>
                        <th>Processing</th>
                    </tr>
                </thead>
                <tbody id="jobsStats_tableBody">
                    <tr><td colspan="5" class="text-muted">No data</td></tr>
                </tbody>
            </table>
        </div>

        <script>
            let jobsStatsTimer = null;
            let orchestratorTimer = null;
            let orchestratorPrev = null; // { ts, stages: {chunking:{dlq,q,proc,fail}, ...} }
            let orchestratorSSEController = null;
            let orchestratorSSEEnabled = false;

            function setOrchestratorFallbackBadge(show, text) {
                const el = document.getElementById('orchestrator_fallback_badge');
                const hint = document.getElementById('orchestrator_fallback_hint');
                if (!el) return;
                if (show) {
                    if (text) el.textContent = text;
                    el.style.display = 'inline-block';
                    if (hint) hint.style.display = 'inline-block';
                } else {
                    el.style.display = 'none';
                    if (hint) hint.style.display = 'none';
                }
            }

            function setOrchestratorSSEStatus(connected) {
                const el = document.getElementById('orchestrator_sse_status');
                if (!el) return;
                if (connected) {
                    el.classList.remove('badge-secondary');
                    el.classList.add('badge-success');
                    el.textContent = 'live';
                } else {
                    el.classList.remove('badge-success');
                    el.classList.add('badge-secondary');
                    el.textContent = 'disconnected';
                }
            }

            function updateOrchestratorFromPayload(res) {
                const q = (res && res.queues) || {};
                const d = (res && res.dlq) || {};
                const s = (res && res.stages) || {};
                const flags = (res && res.flags) || {};
                const ages = (res && res.ages) || {};
                // Detect zeroed fallback (summary endpoint graceful fallback)
                const isZeroed = (!Object.keys(q).length && !Object.keys(d).length && !Object.keys(s).length && !Object.keys(flags).length && !Object.keys(ages).length);
                setOrchestratorFallbackBadge(isZeroed, 'fallback');
                const stages = ['chunking','embedding','storage'];
                const now = (res && res.ts) ? Number(res.ts) : (Date.now()/1000.0);
                const prev = orchestratorPrev;
                orchestratorHistory = orchestratorHistory || { chunking: [], embedding: [], storage: [] };
                window.orchestratorAgeHistory = window.orchestratorAgeHistory || { chunking: [], embedding: [], storage: [] };
                const rows = stages.map(st => {
                    const qn = `embeddings:${st}`;
                    const dq = `embeddings:${st}:dlq`;
                    const qdepth = q[qn] || 0;
                    const ddepth = d[dq] || 0;
                    const ss = s[st] || { processed: 0, failed: 0 };
                    const ageVal = ages[qn] || 0;
                    let dlqRate = 0.0, procRate = 0.0, failRate = 0.0;
                    if (prev && prev.stages && prev.stages[st] && prev.ts) {
                        const dt = Math.max(0.001, now - prev.ts);
                        const p = prev.stages[st];
                        dlqRate = Math.max(0, (ddepth - p.dlq) / dt);
                        procRate = Math.max(0, (ss.processed - p.processed) / dt);
                        failRate = Math.max(0, (ss.failed - p.failed) / dt);
                    }
                    const hist = orchestratorHistory[st] || [];
                    hist.push(ddepth);
                    while (hist.length > 30) hist.shift();
                    orchestratorHistory[st] = hist;
                    const ageHist = window.orchestratorAgeHistory[st] || [];
                    ageHist.push(ageVal);
                    while (ageHist.length > 30) ageHist.shift();
                    window.orchestratorAgeHistory[st] = ageHist;
                    const fl = flags[st] || {};
                    const flagTxt = [fl.paused ? 'paused' : '', fl.drain ? 'drain' : ''].filter(Boolean).join(' ') || '-';
                    return `<tr>
                        <td>${st}</td>
                        <td>${qdepth}</td>
                        <td>${ddepth}</td>
                        <td><svg id="spark-${st}" width="120" height="24" viewBox="0 0 120 24"></svg></td>
                        <td>${dlqRate.toFixed(2)}</td>
                        <td><svg id="age-spark-${st}" width="120" height="24" viewBox="0 0 120 24"></svg></td>
                        <td>${flagTxt}</td>
                        <td>${ss.processed}</td>
                        <td>${procRate.toFixed(2)}</td>
                        <td>${ss.failed}</td>
                        <td>${failRate.toFixed(2)}</td>
                    </tr>`;
                }).join('');
                const tb = document.getElementById('orchestrator_tableBody');
                tb.innerHTML = rows || '<tr><td colspan="8" class="text-muted">No data</td></tr>';
                try {
                    const render = (elId, data) => {
                        const svg = document.getElementById(elId);
                        if (!svg || !data || data.length < 2) return;
                        const w = 120, h = 24, pad = 1;
                        const min = Math.min.apply(null, data);
                        const max = Math.max.apply(null, data);
                        const range = Math.max(1, max - min);
                        const step = (w - pad * 2) / (data.length - 1);
                        let dpath = '';
                        data.forEach((v, i) => {
                            const x = pad + i * step;
                            const y = h - pad - ((v - min) / range) * (h - pad * 2);
                            dpath += (i === 0 ? 'M' : 'L') + x.toFixed(2) + ' ' + y.toFixed(2) + ' ';
                        });
                        svg.innerHTML = `<path d="${dpath}" fill="none" stroke="currentColor" stroke-width="1" />`;
                    };
                    render('spark-chunking', orchestratorHistory.chunking);
                    render('spark-embedding', orchestratorHistory.embedding);
                    render('spark-storage', orchestratorHistory.storage);
                    render('age-spark-chunking', window.orchestratorAgeHistory.chunking);
                    render('age-spark-embedding', window.orchestratorAgeHistory.embedding);
                    render('age-spark-storage', window.orchestratorAgeHistory.storage);
                } catch (e) { /* ignore */ }
                orchestratorPrev = {
                    ts: now,
                    stages: {
                        chunking: { dlq: d['embeddings:chunking:dlq'] || 0, processed: (s['chunking']||{}).processed||0, failed: (s['chunking']||{}).failed||0 },
                        embedding: { dlq: d['embeddings:embedding:dlq'] || 0, processed: (s['embedding']||{}).processed||0, failed: (s['embedding']||{}).failed||0 },
                        storage: { dlq: d['embeddings:storage:dlq'] || 0, processed: (s['storage']||{}).processed||0, failed: (s['storage']||{}).failed||0 }
                    }
                };
            }
            async function fetchOrchestratorSummary() {
                // Prefer richer summary (includes ages/flags); fallback to legacy stats
                try {
                    const res0 = await apiClient.get('/api/v1/embeddings/orchestrator/summary');
                    updateOrchestratorFromPayload(res0);
                    return;
                } catch (e) { /* ignore and fallback */ }
                try {
                    const res = await apiClient.get('/api/v1/embeddings/dlq/stats');
                    // Legacy stats fallback does not expose ages/flags; hide zeroed badge
                    setOrchestratorFallbackBadge(false);
                    const q = (res && res.queues) || {};
                    const d = (res && res.dlq) || {};
                    const s = (res && res.stages) || {};
                    const stages = ['chunking','embedding','storage'];
                    const now = Date.now() / 1000.0;
                    const prev = orchestratorPrev;
                    orchestratorHistory = orchestratorHistory || { chunking: [], embedding: [], storage: [] };
                    const rows = stages.map(st => {
                        const qn = `embeddings:${st}`;
                        const dq = `embeddings:${st}:dlq`;
                        const qdepth = q[qn] || 0;
                        const ddepth = d[dq] || 0;
                        const ss = s[st] || { processed: 0, failed: 0 };
                        // Compute rates if previous sample exists
                        let dlqRate = 0.0, procRate = 0.0, failRate = 0.0;
                        if (prev && prev.stages && prev.stages[st] && prev.ts) {
                            const dt = Math.max(0.001, now - prev.ts);
                            const p = prev.stages[st];
                            dlqRate = Math.max(0, (ddepth - p.dlq) / dt);
                            procRate = Math.max(0, (ss.processed - p.processed) / dt);
                            failRate = Math.max(0, (ss.failed - p.failed) / dt);
                        }
                        // Update sparkline data (keep last 30)
                        const hist = orchestratorHistory[st] || [];
                        hist.push(ddepth);
                        while (hist.length > 30) hist.shift();
                        orchestratorHistory[st] = hist;

                        return `<tr>
                            <td>${st}</td>
                            <td>${qdepth}</td>
                            <td>${ddepth}</td>
                            <td><svg id="spark-${st}" width="120" height="24" viewBox="0 0 120 24"></svg></td>
                            <td>${dlqRate.toFixed(2)}</td>
                            <td>${ss.processed}</td>
                            <td>${procRate.toFixed(2)}</td>
                            <td>${ss.failed}</td>
                            <td>${failRate.toFixed(2)}</td>
                        </tr>`;
                    }).join('');
                    const tb = document.getElementById('orchestrator_tableBody');
                    tb.innerHTML = rows || '<tr><td colspan="8" class="text-muted">No data</td></tr>';
                    // Render sparklines
                    try {
                        const render = (elId, data) => {
                            const svg = document.getElementById(elId);
                            if (!svg || !data || data.length < 2) return;
                            const w = 120, h = 24, pad = 1;
                            const min = Math.min.apply(null, data);
                            const max = Math.max.apply(null, data);
                            const range = Math.max(1, max - min);
                            const step = (w - pad * 2) / (data.length - 1);
                            let dpath = '';
                            data.forEach((v, i) => {
                                const x = pad + i * step;
                                const y = h - pad - ((v - min) / range) * (h - pad * 2);
                                dpath += (i === 0 ? 'M' : 'L') + x.toFixed(2) + ' ' + y.toFixed(2) + ' ';
                            });
                            svg.innerHTML = `<path d="${dpath}" fill="none" stroke="currentColor" stroke-width="1" />`;
                        };
                        render('spark-chunking', orchestratorHistory.chunking);
                        render('spark-embedding', orchestratorHistory.embedding);
                        render('spark-storage', orchestratorHistory.storage);
                    } catch (e) { /* ignore */ }

                    // Save snapshot for next rate computation
                    orchestratorPrev = {
                        ts: now,
                        stages: {
                            chunking: { dlq: d['embeddings:chunking:dlq'] || 0, processed: (s['chunking']||{}).processed||0, failed: (s['chunking']||{}).failed||0 },
                            embedding: { dlq: d['embeddings:embedding:dlq'] || 0, processed: (s['embedding']||{}).processed||0, failed: (s['embedding']||{}).failed||0 },
                            storage: { dlq: d['embeddings:storage:dlq'] || 0, processed: (s['storage']||{}).processed||0, failed: (s['storage']||{}).failed||0 }
                        }
                    };
                } catch (e) {
                    const tb = document.getElementById('orchestrator_tableBody');
                    tb.innerHTML = `<tr><td colspan="8">${Utils.escapeHtml(JSON.stringify(e.response || e))}</td></tr>`;
                }
            }
            function startOrchestratorAutoRefresh() {
                stopOrchestratorAutoRefresh();
                fetchOrchestratorSummary();
                orchestratorTimer = setInterval(fetchOrchestratorSummary, 10000);
                try { Utils.saveToStorage('orchestrator-auto-refresh', true); } catch (e) { /* ignore */ }
            }
            function stopOrchestratorAutoRefresh() {
                if (orchestratorTimer) {
                    clearInterval(orchestratorTimer);
                    orchestratorTimer = null;
                }
                try { Utils.saveToStorage('orchestrator-auto-refresh', false); } catch (e) { /* ignore */ }
            }

            async function startOrchestratorSSE() {
                try {
                    const baseUrl = (window.apiClient && window.apiClient.baseUrl) ? window.apiClient.baseUrl : window.location.origin;
                    const token = (window.apiClient && window.apiClient.token) ? window.apiClient.token : '';
                    orchestratorSSEController = new AbortController();
                    const res = await fetch(`${baseUrl}/api/v1/embeddings/orchestrator/events`, {
                        method: 'GET',
                        headers: {
                            ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
                        },
                        signal: orchestratorSSEController.signal,
                    });
                    orchestratorSSEEnabled = true;
                    setOrchestratorSSEStatus(true);
                    await apiClient.handleStreamingResponse(res, (chunk) => {
                        if (!orchestratorSSEEnabled) return;
                        if (chunk && !chunk.error) {
                            updateOrchestratorFromPayload(chunk);
                        }
                    });
                } catch (e) {
                    // ignore abort/network
                }
            }
            function stopOrchestratorSSE() {
                orchestratorSSEEnabled = false;
                if (orchestratorSSEController) {
                    try { orchestratorSSEController.abort(); } catch (e) { /* ignore */ }
                    orchestratorSSEController = null;
                }
                setOrchestratorSSEStatus(false);
            }
            function toggleOrchestratorSSE(cb) {
                if (cb && cb.checked) {
                    stopOrchestratorAutoRefresh();
                    startOrchestratorSSE();
                } else {
                    stopOrchestratorSSE();
                }
                try { Utils.saveToStorage('orchestrator-sse-enabled', !!(cb && cb.checked)); } catch (e) { /* ignore */ }
            }

            function openMonitoringDocs(ev){
                try { if (ev && ev.preventDefault) ev.preventDefault(); } catch(e){}
                // Try a few likely locations; fall back to GitHub path or alert with instructions.
                const candidates = [
                    window.location.origin + '/monitoring/README.md',
                    window.location.origin + '/webui/monitoring/README.md',
                ];
                const gh = 'https://github.com/rmusser01/tldw_server/blob/main/monitoring/README.md';
                // Attempt to open the first candidate in a new tab; the server may not expose it, so also show a tip
                try { window.open(candidates[0], '_blank'); } catch(e){}
                try {
                    alert('Monitoring docs are available at monitoring/README.md in the repo.\n' +
                          'If browsing online, see: ' + gh + '\n' +
                          'Prometheus text metrics: /api/v1/metrics/text');
                } catch(e){}
            }

            // Initialize auto-refresh from saved preference
            try {
                const saved = Utils.getFromStorage('orchestrator-auto-refresh');
                if (saved === true) {
                    startOrchestratorAutoRefresh();
                }
            } catch (e) { /* ignore */ }
            // Initialize SSE preference
            try {
                const savedSSE = Utils.getFromStorage('orchestrator-sse-enabled');
                const cb = document.getElementById('orchestrator_live_sse');
                if (savedSSE && cb) {
                    cb.checked = true;
                    stopOrchestratorAutoRefresh();
                    startOrchestratorSSE();
                }
            } catch (e) { /* ignore */ }
            async function fetchJobsStats() {
                const domain = document.getElementById('jobsStats_domain').value.trim();
                const queue = document.getElementById('jobsStats_queue').value.trim();
                const jobType = document.getElementById('jobsStats_jobType').value.trim();
                const query = {};
                if (domain) query.domain = domain;
                if (queue) query.queue = queue;
                if (jobType) query.job_type = jobType;
                const tbody = document.getElementById('jobsStats_tableBody');
                try {
                    Loading.show(tbody.parentElement, 'Loading jobs stats...');
                    const res = await apiClient.makeRequest('GET', '/api/v1/jobs/stats', { query });
                    const data = Array.isArray(res) ? res : (res?.data || []);
                    tbody.innerHTML = '';
                    if (!data.length) {
                        tbody.innerHTML = '<tr><td colspan="5" class="text-muted">No data</td></tr>';
                        return;
                    }
                    for (const row of data) {
                        const tr = document.createElement('tr');
                        tr.innerHTML = `
                            <td>${row.domain ?? ''}</td>
                            <td>${row.queue ?? ''}</td>
                            <td>${row.job_type ?? ''}</td>
                            <td>${row.queued ?? 0}</td>
                            <td>${row.processing ?? 0}</td>
                        `;
                        tbody.appendChild(tr);
                    }
                } catch (e) {
                    tbody.innerHTML = `<tr><td colspan="5" class="text-error">${(e && e.message) || 'Failed to load'}</td></tr>`;
                } finally {
                    Loading.hide(tbody.parentElement);
                }
            }
            function startJobsStatsAutoRefresh() {
                stopJobsStatsAutoRefresh();
                jobsStatsTimer = setInterval(fetchJobsStats, 10000);
                fetchJobsStats();
            }
            function stopJobsStatsAutoRefresh() {
                if (jobsStatsTimer) {
                    clearInterval(jobsStatsTimer);
                    jobsStatsTimer = null;
                }
            }
        </script>
    </div>
</div>

<!-- Metrics Analysis Tab -->
<div id="tabMetricsAnalysis" class="tab-content">
    <div class="endpoint-section">
        <h2>Metrics Analysis Tools</h2>
        <p>Analyze historical metrics data and generate insights.</p>

        <div class="columns">
            <div class="column">
                <div class="form-group">
                    <label for="metricsAnalysis_timeRange">Time Range:</label>
                    <select id="metricsAnalysis_timeRange">
                        <option value="1h">Last Hour</option>
                        <option value="6h">Last 6 Hours</option>
                        <option value="24h" selected>Last 24 Hours</option>
                        <option value="7d">Last 7 Days</option>
                        <option value="30d">Last 30 Days</option>
                    </select>
                </div>
            </div>
            <div class="column">
                <div class="form-group">
                    <label for="metricsAnalysis_metric">Metric Type:</label>
                    <select id="metricsAnalysis_metric">
                        <option value="response_time">Response Time</option>
                        <option value="error_rate">Error Rate</option>
                        <option value="throughput">Throughput</option>
                        <option value="cpu_usage">CPU Usage</option>
                        <option value="memory_usage">Memory Usage</option>
                    </select>
                </div>
            </div>
        </div>

        <div class="form-group">
            <label for="metricsAnalysis_aggregation">Aggregation:</label>
            <select id="metricsAnalysis_aggregation">
                <option value="avg">Average</option>
                <option value="min">Minimum</option>
                <option value="max">Maximum</option>
                <option value="p50">Median (p50)</option>
                <option value="p95">95th Percentile</option>
                <option value="p99">99th Percentile</option>
            </select>
        </div>

        <button class="btn btn-primary" id="metricsAnalysis_run">
            Run Analysis
        </button>

        <div id="metricsAnalysis_chart" class="chart-container mt-3">
            <!-- Chart will be rendered here -->
        </div>

        <div id="metricsAnalysis_insights" class="insights-panel mt-3">
            <!-- Insights will appear here -->
        </div>
    </div>

    <div class="endpoint-section mt-4">
        <h2>Alerts Configuration</h2>
        <p>Set up alerts based on metric thresholds.</p>

        <div class="form-group">
            <label for="metricsAlerts_config">Alert Configuration (JSON):</label>
            <textarea id="metricsAlerts_config" class="code-input" rows="20">{
  "alerts": [
    {
      "name": "High Error Rate",
      "metric": "error_rate",
      "condition": "greater_than",
      "threshold": 5,
      "duration_minutes": 5,
      "severity": "critical",
      "notification": {
        "email": "admin@example.com",
        "webhook": "https://hooks.example.com/alerts"
      }
    },
    {
      "name": "High Response Time",
      "metric": "response_time_p95",
      "condition": "greater_than",
      "threshold": 1000,
      "duration_minutes": 10,
      "severity": "warning"
    },
    {
      "name": "Low Throughput",
      "metric": "throughput",
      "condition": "less_than",
      "threshold": 100,
      "duration_minutes": 15,
      "severity": "info"
    }
  ]
}</textarea>
        </div>

        <button class="btn btn-primary" id="metricsAlerts_save">
            Save Alerts
        </button>
    </div>
</div>

<script>
let metricsAutoRefresh = null;

function refreshMetrics() {
    // Fetch and update metrics display
    fetch('/api/v1/metrics')
        .then(response => response.json())
        .then(data => {
            // Update metric cards with data
            document.getElementById('metric-api-health').textContent = data.health || 'Healthy';
            document.getElementById('metric-request-rate').textContent = data.request_rate || '0';
            document.getElementById('metric-response-time').textContent = data.response_time_p50 || '0';
            document.getElementById('metric-active-connections').textContent = data.active_connections || '0';
            document.getElementById('metric-error-rate').textContent = data.error_rate || '0';
            document.getElementById('metric-cpu-usage').textContent = (data.cpu_usage || 0).toFixed(1) + '%';
            document.getElementById('metric-memory-usage').textContent = (data.memory_mb || 0).toFixed(0);
            document.getElementById('metric-database-size').textContent = (data.database_mb || 0).toFixed(1);
        })
        .catch(error => {
            console.error('Failed to fetch metrics:', error);
        });

    // Also refresh Jobs overview widget
    refreshJobsOverview();
}

function startAutoRefresh() {
    if (metricsAutoRefresh) {
        clearInterval(metricsAutoRefresh);
    }
    refreshMetrics(); // Initial refresh
    metricsAutoRefresh = setInterval(refreshMetrics, 5000);
    Toast.success('Auto-refresh enabled (5 seconds)');
}

function stopAutoRefresh() {
    if (metricsAutoRefresh) {
        clearInterval(metricsAutoRefresh);
        metricsAutoRefresh = null;
        Toast.info('Auto-refresh stopped');
    }
}

function escapeHTML(str) {
    return String(str)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
}

function runMetricsAnalysis() {
    const timeRange = document.getElementById('metricsAnalysis_timeRange').value;
    const metric = document.getElementById('metricsAnalysis_metric').value;
    const aggregation = document.getElementById('metricsAnalysis_aggregation').value;

    // Escape variables from user input
    const safeMetric = escapeHTML(metric);
    const safeAggregation = escapeHTML(aggregation);
    const safeTimeRange = escapeHTML(timeRange);

    // Simulate analysis results
    const chartContainer = document.getElementById('metricsAnalysis_chart');
    chartContainer.innerHTML = `
        <div class="chart-placeholder">
            <p>Chart: ${safeMetric} over ${safeTimeRange} (${safeAggregation})</p>
            <div class="chart-bars">
                <div class="chart-bar" style="height: 60%;"></div>
                <div class="chart-bar" style="height: 80%;"></div>
                <div class="chart-bar" style="height: 45%;"></div>
                <div class="chart-bar" style="height: 90%;"></div>
                <div class="chart-bar" style="height: 70%;"></div>
                <div class="chart-bar" style="height: 85%;"></div>
                <div class="chart-bar" style="height: 50%;"></div>
            </div>
        </div>
    `;

    const insightsPanel = document.getElementById('metricsAnalysis_insights');
    insightsPanel.innerHTML = `
        <div class="insights">
            <h4>Analysis Insights</h4>
            <ul>
                <li>Peak ${safeMetric} occurred at 14:30 UTC</li>
                <li>Average ${safeAggregation} value: 234ms</li>
                <li>Detected anomaly at 12:15 UTC (3σ deviation)</li>
                <li>Trend: Increasing by 12% over selected period</li>
            </ul>
        </div>
    `;
}

function saveMetricsAlerts() {
    const config = document.getElementById('metricsAlerts_config').value;
    // Would save alerts configuration
    Toast.success('Alerts configuration saved');
}

// Initialize metrics on tab load
document.addEventListener('DOMContentLoaded', () => {
    if (document.querySelector('#tabMetricsDashboard.active')) {
        refreshMetrics();
    }
});

async function refreshJobsOverview() {
    try {
        const res = await apiClient.makeRequest('GET', '/api/v1/jobs/stats', { query: {} });
        const data = Array.isArray(res) ? res : (res?.data || []);
        const ul = document.getElementById('metric-jobs-overview');
        ul.innerHTML = '';
        if (!data.length) {
            ul.innerHTML = '<li class="text-muted">No data</li>';
            return;
        }
        const scored = data.map(r => ({
            domain: r.domain || '',
            queue: r.queue || '',
            job_type: r.job_type || '',
            queued: r.queued || 0,
            processing: r.processing || 0,
        })).map(r => ({ ...r, score: (r.queued + r.processing) }));
        scored.sort((a, b) => b.score - a.score);
        const top = scored.slice(0, 3);
        for (const row of top) {
            const li = document.createElement('li');
            const a = document.createElement('a');
            a.href = '#';
            a.textContent = `${row.domain}/${row.queue}/${row.job_type} - queued: ${row.queued}, processing: ${row.processing}`;
            a.addEventListener('click', (ev) => {
                ev.preventDefault();
                openAdminJobsWithFilter(row.domain, row.queue, row.job_type);
            });
            li.appendChild(a);
            ul.appendChild(li);
        }
        if (!ul.children.length) {
            ul.innerHTML = '<li class="text-muted">No active queues</li>';
        }
    } catch (e) {
        // Fallback: show soft error but keep dashboard
        const ul = document.getElementById('metric-jobs-overview');
        if (ul) ul.innerHTML = '<li class="text-error">Jobs overview unavailable</li>';
    }
}

// Open Admin → Jobs and apply filters, then refresh table
function openAdminJobsWithFilter(domain, queue, jobType) {
    try {
        // Switch to Admin top tab
        const topAdminBtn = document.querySelector('.top-tab-button[data-toptab="admin"]');
        if (topAdminBtn) topAdminBtn.click();

        // After a short delay, switch to Admin Jobs sub-tab
        setTimeout(() => {
            const adminJobsBtn = document.querySelector('#admin-subtabs .sub-tab-button[data-content-id="tabAdminJobs"]');
            if (adminJobsBtn) adminJobsBtn.click();

            // Wait for inputs to be available, then set values and refresh
            let attempts = 0;
            const tryApply = () => {
                const d = document.getElementById('adminJobs_domain');
                const q = document.getElementById('adminJobs_queue');
                const jt = document.getElementById('adminJobs_jobType');
                if (d && q && jt) {
                    d.value = domain || '';
                    q.value = queue || '';
                    jt.value = jobType || '';
                    const topbar = document.getElementById('adminJobs_topbar');
                    if (topbar) {
                        const desc = `${domain || '(any)'}/${queue || '(any)'}/${jobType || '(any)'}`;
                        topbar.textContent = `Filter applied: ${desc}`;
                    }
                    try { if (typeof adminJobsSaveFilters === 'function') adminJobsSaveFilters(); } catch (e) { /* ignore */ }
                    try { if (typeof updateSavedFiltersBadge === 'function') updateSavedFiltersBadge({ domain, queue, job_type: jobType }); } catch (e) { /* ignore */ }
                    if (typeof adminFetchJobsStats === 'function') adminFetchJobsStats();
                    return true;
                }
                return false;
            };
            const interval = setInterval(() => {
                attempts += 1;
                if (tryApply() || attempts > 20) {
                    clearInterval(interval);
                }
            }, 100);
        }, 100);
    } catch (e) {
        console.warn('Failed to navigate to Admin → Jobs with filter', e);
    }
}
</script>

<style>
.metrics-controls {
    display: flex;
    gap: var(--spacing-md);
}

.metrics-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--spacing-lg);
}

.metric-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--spacing-lg);
    text-align: center;
    transition: var(--transition-fast);
}

.metric-card:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.metric-card h3 {
    font-size: 0.9rem;
    color: var(--color-text-muted);
    margin: 0 0 var(--spacing-sm) 0;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.metric-value {
    font-size: 2rem;
    font-weight: 700;
    color: var(--color-primary);
    margin: var(--spacing-sm) 0;
}

.metric-label {
    font-size: 0.85rem;
    color: var(--color-text-secondary);
}

.chart-container {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--spacing-lg);
    min-height: 300px;
}

.chart-placeholder {
    text-align: center;
}

.chart-bars {
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    height: 200px;
    margin-top: var(--spacing-lg);
}

.chart-bar {
    width: 40px;
    background: var(--color-primary);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    transition: var(--transition-fast);
}

.chart-bar:hover {
    background: var(--color-primary-dark);
}

.insights-panel {
    background: var(--color-surface-alt);
    border-radius: var(--radius-md);
    padding: var(--spacing-lg);
}

.insights h4 {
    margin-top: 0;
    color: var(--color-text);
}

.insights ul {
    margin: var(--spacing-md) 0 0 0;
    padding-left: var(--spacing-lg);
}

.insights li {
    margin: var(--spacing-sm) 0;
    color: var(--color-text-secondary);
}

.btn-warning {
    background: var(--color-warning);
    color: white;
}

.btn-warning:hover {
    background: var(--color-warning-dark);
}
</style>
